06 json处理
做Web开发、调API、读配置文件,几乎天天都要和JSON打交道。JSON是一种轻量级的数据交换格式,长得像Python的字典和列表,但本质上是字符串。
Python的json模块就是专门处理JSON的——把Python对象转成JSON字符串(序列化),或者把JSON字符串转成Python对象(反序列化)。
一、JSON与Python类型映射
JSON和Python的数据类型有对应关系:
| JSON类型 | Python类型 |
|---|---|
object {} | dict |
array [] | list |
string "" | str |
| number | int / float |
| true / false | True / False |
| null | None |
二、序列化:Python → JSON
2.1 json.dumps()
把Python对象转成JSON字符串。
python
import json
# 字典转JSON
data = {"name": "大志", "age": 28, "active": True}
json_str = json.dumps(data)
print(json_str)
# {"name": "\u5927\u5fd7", "age": 28, "active": true}默认情况下,中文会被转义成Unicode。要保留中文,设置ensure_ascii=False:
python
json_str = json.dumps(data, ensure_ascii=False)
print(json_str)
# {"name": "大志", "age": 28, "active": true}2.2 格式化输出
python
import json
data = {"name": "大志", "skills": ["Python", "LangChain"], "age": 28}
# 美化输出(缩进)
print(json.dumps(data, ensure_ascii=False, indent=2))输出:
json
{
"name": "大志",
"skills": [
"Python",
"LangChain"
],
"age": 28
}2.3 关键参数
python
import json
data = {"name": "大志", "age": 28, "skills": ["Python", "LangChain"]}
# indent:缩进级别
json.dumps(data, indent=2)
# separators:分隔符(默认(', ', ': '))
json.dumps(data, separators=(',', ':')) # 紧凑输出
# sort_keys:按键排序
json.dumps(data, sort_keys=True, ensure_ascii=False)
# ensure_ascii:是否转义非ASCII字符
json.dumps(data, ensure_ascii=False) # 保留中文2.4 json.dump()
把Python对象直接写入文件。
python
import json
data = {"name": "大志", "age": 28}
# 写入JSON文件
with open("data.json", "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=2)三、反序列化:JSON → Python
3.1 json.loads()
把JSON字符串转成Python对象。
python
import json
json_str = '{"name": "大志", "age": 28, "active": true}'
data = json.loads(json_str)
print(data)
# {'name': '大志', 'age': 28, 'active': True}
print(type(data)) # <class 'dict'>
print(data["name"]) # 大志3.2 json.load()
从文件读取JSON。
python
import json
with open("data.json", "r", encoding="utf-8") as f:
data = json.load(f)
print(data)3.3 解析嵌套JSON
python
import json
json_str = '''
{
"agent": {
"name": "大志助手",
"model": "gpt-5",
"tools": ["search", "calculator", "code"]
},
"version": 1.0
}
'''
config = json.loads(json_str)
# 访问嵌套数据
config["agent"]["name"] # '大志助手'
config["agent"]["tools"][0] # 'search'3.4 解析JSON数组
python
import json
json_str = '[{"name": "大志", "age": 28}, {"name": "小明", "age": 25}]'
users = json.loads(json_str)
for user in users:
print(f"{user['name']}: {user['age']}岁")四、处理特殊类型
json模块默认只能处理基本类型(dict、list、str、int、float、bool、None)。遇到其他类型会报错:
python
import json
from datetime import datetime
data = {"time": datetime.now()}
json.dumps(data) # TypeError: Object of type datetime is not JSON serializable4.1 自定义序列化
用default参数指定一个转换函数:
python
import json
from datetime import datetime
def default_serializer(obj):
"""自定义序列化函数"""
if isinstance(obj, datetime):
return obj.isoformat()
raise TypeError(f"Object of type {type(obj)} is not JSON serializable")
data = {"time": datetime.now(), "name": "大志"}
json_str = json.dumps(data, default=default_serializer, ensure_ascii=False)
print(json_str)
# {"time": "2026-06-13T10:30:00.123456", "name": "大志"}4.2 处理Decimal
python
import json
from decimal import Decimal
data = {"price": Decimal("99.99")}
# 方法1:转成字符串
json.dumps(data, default=str)
# 方法2:转成float
json.dumps(data, default=lambda x: float(x) if isinstance(x, Decimal) else None)4.3 自定义反序列化
用object_hook参数把JSON对象转成自定义类型:
python
import json
class User:
def __init__(self, name, age):
self.name = name
self.age = age
def user_hook(dct):
"""把JSON对象转成User对象"""
if "name" in dct and "age" in dct:
return User(dct["name"], dct["age"])
return dct
json_str = '{"name": "大志", "age": 28}'
user = json.loads(json_str, object_hook=user_hook)
print(type(user)) # <class 'User'>
print(user.name) # 大志五、处理JSON Lines
JSON Lines是一种常见的日志格式,每行一个JSON对象:
json
{"name": "大志", "age": 28}
{"name": "小明", "age": 25}
{"name": "小红", "age": 23}解析JSON Lines:
python
import json
def parse_jsonl(text):
"""解析JSON Lines格式"""
result = []
for line in text.strip().split("\n"):
if line.strip():
result.append(json.loads(line))
return result
# 从文件读取JSON Lines
with open("data.jsonl", "r", encoding="utf-8") as f:
for line in f:
data = json.loads(line.strip())
print(data)六、命令行工具
Python提供了命令行工具来格式化和验证JSON:
bash
# 格式化JSON文件
python -m json.tool data.json
# 格式化并输出到文件
python -m json.tool data.json > formatted.json
# 验证JSON是否合法
echo '{"name": "test"}' | python -m json.tool
# 管道输入
curl -s https://api.example.com/data | python -m json.tool七、实用技巧
7.1 安全地解析JSON
python
import json
def safe_json_loads(s, default=None):
"""安全地解析JSON,失败时返回默认值"""
try:
return json.loads(s)
except json.JSONDecodeError:
return default
data = safe_json_loads("invalid json", {})7.2 合并多个JSON文件
python
import json
from pathlib import Path
def merge_json_files(directory):
"""合并目录下所有JSON文件"""
result = {}
for p in Path(directory).glob("*.json"):
with open(p, "r", encoding="utf-8") as f:
data = json.load(f)
if isinstance(data, dict):
result.update(data)
return result7.3 JSON美化打印
python
import json
def pretty_json(data):
"""美化打印JSON"""
return json.dumps(data, ensure_ascii=False, indent=2, sort_keys=True)
# 调试时使用
print(pretty_json(api_response))7.4 深度合并JSON
python
import json
def deep_merge(base, override):
"""深度合并两个字典"""
result = base.copy()
for key, value in override.items():
if key in result and isinstance(result[key], dict) and isinstance(value, dict):
result[key] = deep_merge(result[key], value)
else:
result[key] = value
return result
# 合并配置文件
with open("config.default.json") as f:
default_config = json.load(f)
with open("config.json") as f:
user_config = json.load(f)
config = deep_merge(default_config, user_config)7.5 处理大文件
处理大JSON文件时,可以使用ijson库进行流式解析:
python
# pip install ijson
import ijson
# 流式解析大JSON文件
with open("large.json", "rb") as f:
for item in ijson.items(f, "data.item"):
print(item)八、总结
json模块的核心就四个函数:
| 函数 | 作用 |
|---|---|
json.dumps(obj) | Python对象 → JSON字符串 |
json.loads(s) | JSON字符串 → Python对象 |
json.dump(obj, f) | Python对象 → 写入文件 |
json.load(f) | 从文件读取 → Python对象 |
常用参数:
| 参数 | 作用 |
|---|---|
ensure_ascii=False | 保留中文,不转义 |
indent=2 | 美化输出,缩进2格 |
sort_keys=True | 按键名排序 |
default=func | 自定义序列化函数 |
object_hook=func | 自定义反序列化函数 |
记住dumps()和loads()就够了,带s的是操作字符串,不带s的是操作文件。